//=======================================================
/**
 * @description 小程序应用组件代理对象工厂
 * @author Brave Chan on 2019.5
 * @version 2.0.0
 */
//=======================================================
import {
  combineGlobalData,
  clearOriginData
} from './beconst';
import {
  isFunction,
  assignObjExcept,
  getSysId
} from '../utils/util';
import {
  execAry,
  execAsyncList
} from '../utils/execList';
import {
  COMPONENT_TYPE
} from '../constant/nodeType';
import {
  be_invokeOriginPares,
  be_invokeInitVMP,
  be_invokeParse,
  be_invokeDecorate,
  be_invokeWash,
  be_invokeDestroyVMP,
  be_invokeOnShow,
  be_invokeOnHide
} from './BEModules';
import be_opt from '../constant/be_opt';
import Log from '../tools/BELog';
import { loadExecList, saveExecList } from './beGlobal';
//=======================================================
/*
{
  data() {},
  created() {}, // 相当于lifetimes.created,
  mounted() {}, // 相当于lifetimes.attached，创建vmp
  ready() {}, // 相当于lifetimes.ready
  destroyed() {}, // 相当于lifetimes.detached, 销毁vmp
  error() {},
  pageShow() {}, // 相当于pageLifetimes.show, 恢复属性生效
  pageHide() {}, // 相当于pageLifetimes.hide, 暂停属性生效
  pageResize() {}, // 相当于pageLifetimes.resize
  methods: {},
  methodAreas: []
}
*/
//=======================================================

//============================================================
// parse 阶段
function originParse(origin, component) {
  let data = combineGlobalData(origin.data);

  // 调用需要parse阶段处理的模块
  be_invokeOriginPares(component, origin, COMPONENT_TYPE);

  component.data = data;
}

// 预初始化
function preInit(origin, component) {
  component._beID = getSysId();
  component.__initialized__ = false;

  component._beNodeType = COMPONENT_TYPE;
  component.lifetimes = {};
  component.pageLifetimes = {};
}

// 绑定方法
function bindMethods(origin, component) {
  // methods
  component.methods = Object.assign({}, origin.methods || {});

  // methodAreas
  const areas = origin.methodAreas || {};
  let len = areas.length;

  let fns, entries;
  while (len--) {
    fns = areas[len];
    entries = Object.entries(fns);
    for (const [fnName, fn] of entries) {
      if (typeof fn === 'function') {
        component.methods[fnName] = fn;
      }
    }
  }
}

// 包装created, 相当于lifetimes.created
function packCreated(origin, component) {
  component.lifetimes.created = (function (fn) {

    return function () {

      if (isFunction(this._be_data)) {
        this._be_data();
      }

      if (isFunction(fn)) {
        fn.call(this);
      }
    }

  })(origin.created);
}

// 包装attached
function packAttached(origin, component) {
  component.lifetimes.attached = (function (fn) {
    return function () {
      if (!this) {
        Log.error('In Component attached, can not get the "this" obj!!', this);
        if (be_opt.debug) {
          throw new Error('In Component attached, can not get the "this" obj!!');
        }
      } else {
        be_invokeInitVMP(this);
        be_invokeDecorate(this, this._be_vmp);
        be_invokeParse(this, this._be_vmp);
      }
      if (isFunction(fn)) {
        fn.call(this);
      }

      const execList = loadExecList(this._be_init_execs);
      // this._be_init_execs = null;

      // 执行队列之前，让提交的属性立即生效
      if (typeof this.validateNow === 'function') {
        this.validateNow();
      }

      // 执行预定函数队列
      if (Array.isArray(execList)) {
        execAry(this, execList);
      }

      // 执行含有异步的预定函数队列
      if (execList && execList.__isAsync) {
        execAsyncList(this, execList);
      }
    }
  })(origin.mounted);
}

// 包装ready
function packReady(origin, component) {
  component.lifetimes.ready = (function (fn) {
    return function() {
      if (isFunction(fn)) {
        fn.call(this);
      }
    };
  })(origin.ready);
}

// 包装detached
function packDetached(origin, component) {
  component.lifetimes.detached = (function (fn) {
    return function () {
      if (this) {
        be_invokeWash(this, this._be_vmp);
        be_invokeDestroyVMP(this, this._be_vmp);
      }
      if (isFunction(fn)) {
        fn.call(this);
      }
    };
  })(origin.destroyed);
}

// 包装error
function packError(origin, component) {
  component.lifetimes.error = (function (fn) {
    return function (err) {
      Log.error(`Component ${component._beID} happen an error`, err);

      if (isFunction(fn)) {
        fn.call(this, err);
      }
    }
  })(origin.error);
}

// 包装moved
function packMoved(origin, component) {
  component.lifetimes.moved = (function(fn) {
    return function() {
      if (isFunction(fn)) {
        fn.call(this);
      }
    };
  })(origin.moved);
}

// 包装pageLifetimes.show
function packPageShow(origin, component) {
  component.pageLifetimes.show = (function (fn) {

    return function() {
      //when instance onShow, commit the props that collected during onHide be invoked.
      be_invokeOnShow(this, this._be_vmp);

      if (isFunction(fn)) {
        fn.call(this);
      }
    };

  })(origin.pageShow);
}

// 包装pageLifetimes.hide
function packPageHide(origin, component) {
  component.pageLifetimes.hide = (function (fn) {
    return function () {
      // when instance onHide, pause the props validate.
      be_invokeOnHide(this, this._be_vmp);

      if (isFunction(fn)) {
        fn.call(this);
      }
    };
  })(origin.pageHide);
}

// 包装pageLifetimes.resize
function packPageResize(origin, component) {
  component.pageLifetimes.resize = (function(fn) {
    return function(size) {
      if (isFunction(fn)) {
        fn.call(this, size);
      }
    };
  })(origin.resize);
}

//============================================================
/**
 * @public
 * @description 组件代理对象工厂
 * @param {object} component [required] 原始组件对象
 * @returns {object} 合成后的适用于小程序Component()的对象
 */
export default function BEComponent(origin = {}, execList = []) {

  const component = [
    originParse,
    preInit,
    bindMethods,
    packCreated,
    packAttached,
    packDetached,
    packReady,
    packError,
    packMoved,
    packPageShow,
    packPageHide,
    packPageResize
  ].reduce(function(wCom, item) {
    item(origin, wCom);
    return wCom;
  }, {});

  const be_data = {
    _beID: component._beID,
    _beNodeType: component._beNodeType,
  };

  if (execList && (Array.isArray(execList) || !!execList.__isAsync)) {
    component._be_init_execs = component._beID;
    be_data._be_init_execs = component._be_init_execs;
    saveExecList(component._beID, execList);
  }

  let result = assignObjExcept(component, origin, [
    'created',
    'attached',
    'detached',
    'ready',
    'moved',
    'error',
    'lifetimes',
    'pageLifetimes',
    'pageShow',
    'pageHide',
    'pageResize',
    'methods',
    'methodAreas',
    'data'
  ]);

  component.methods._be_data = (function(be_data) {
    return function() {
      Object.assign(this, be_data);
    };
  })(be_data);

  clearOriginData(origin);
  origin = null;
  return result;
};
//=======================================================
